package iiris

import (
	"gitee.com/tietang/terrace-go/v2/base"
	"gitee.com/tietang/terrace-go/v2/base/starter/iweb"
	"github.com/bmatcuk/doublestar"
	"github.com/kataras/iris/v12"
	"github.com/patrickmn/go-cache"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type IrisWebServerStarter struct {
	base.BaseStarter
}

func (s *IrisWebServerStarter) Init(ctx base.StarterContext) {
	conf := ctx.Props()
	iweb.InjectEnv(conf)
}
func (s *IrisWebServerStarter) Setup(ctx base.StarterContext) {
	app := Iris()
	app.Favicon("./favicon.ico")
	conf := ctx.Props()
	healthAndInfoEndpoint(conf, app)
	staticCache := cache.New(24*time.Hour, 240*time.Hour)
	dirs := conf.Strings("ui.static.dirs")
	staticHandler := &StaticHandler{
		dirs:        dirs,
		staticCache: staticCache,
	}
	for _, dir := range dirs {
		app.HandleMany("GET", "/"+dir, staticHandler.handler)
	}
}

type StaticHandler struct {
	dirs        []string
	staticCache *cache.Cache
}

func (g *StaticHandler) handler(ctx iris.Context) {
	uriPath := ctx.Path() // r.URL.Path

	dirs := g.dirs
	staticCache := g.staticCache

	uriPath = strings.TrimPrefix(uriPath, "/console")
	cached := false
	file := "/index.html"
	for _, dir := range dirs {
		p := strings.TrimPrefix(uriPath, "/")
		if strings.HasPrefix(p, dir) || isExcluded(uriPath) {
			file = uriPath
		}
	}
	ext := filepath.Ext(file)
	//排除html不304，其他静态文件，如果不是第一次请求，304
	if ext != ".html" && ext != ".htm" {
		if status := isWriteModified(ctx); status > 0 {
			ctx.StatusCode(status)
			return
		}
	}

	switch {
	case strings.HasSuffix(uriPath, ".css"):
		ctx.Header("content-type", "text/css")
	case strings.HasSuffix(uriPath, ".js"):
		ctx.Header("content-type", "text/javascript")
	default:
	}
	v, ok := staticCache.Get(file)
	status := http.StatusOK
	var fd []byte
	if ok {
		fd = v.([]byte)
		cached = true
		status = http.StatusOK
	} else {

		fd, ok = fileWrite(file)
		cached = false
		if ok {
			staticCache.Set(file, fd, -1)
			status = http.StatusOK
		} else {
			status = http.StatusNotFound
			ctx.StatusCode(status)
		}
	}
	log.WithFields(log.Fields{
		"status":   status,
		"cached":   cached,
		"URL":      uriPath,
		"filePath": file,
	}).Info("Access: ")
	ctx.Write(fd)

}

func isWriteModified(ctx iris.Context) int {

	v := ctx.GetHeader("If-Modified-Since")
	if v == "" {
		ctx.Header("Last-Modified", time.Now().Format(iweb.GMT))
	} else {
		return http.StatusNotModified
	}
	return -1
}
func fileWrite(file string) ([]byte, bool) {
	fin, err := os.Open("." + file)
	defer fin.Close()
	pwd, _ := os.Getwd()
	ok := true
	if err != nil {
		log.Error("open file:", err, pwd, file)
		ok = false
	}
	fd, err := ioutil.ReadAll(fin)
	if err != nil {
		log.Error("read file:", err, pwd, file)
		ok = false
	}
	if ok {
		return fd, true
	} else {
		return []byte("not found file."), false
	}
}

func isExcluded(urlPath string) bool {
	for _, k := range iweb.Exclueds {
		ok, _ := doublestar.PathMatch(k, urlPath)
		if ok {
			return true
		}
	}
	return false
}
